package com.kinsin.kinsinlogsystem;

import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.RoundingMode;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class FileUtils {
    private static final String TAG = "FileUtils";
    private static final int BUFFER_SIZE = 8192;
    private static final int MAX_RECURSION_DEPTH = 50;

    /**
     * 创建文件
     * @param path 路径
     * @return 创建是否成功
     */
    public static boolean createFile(String path) {
        File file = new File(path);
        if (file.exists()) return false;

        try {
            File parent = file.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            return file.createNewFile();
        } catch (IOException e) {
            Log.e(TAG, "Create file error: " + path, e);
            return false;
        }
    }

    /**
     * 创建目录
     * @param path 路径
     * @return 创建是否成功
     */
    public static boolean createDir(String path) {
        File dir = new File(path);
        if (dir.exists()) {
            Log.i(TAG, "createDir: 文件已存在，是否为目录：" + dir.isDirectory());
            return dir.isDirectory();
        } else {
            Log.i(TAG, "createDir: 目录不存在，开始创建目录");
        }
        boolean isCreated = dir.mkdirs();
        if (isCreated) {
            Log.i(TAG, "createDir: 目录创建成功");
        } else {
            Log.i(TAG, "createDir: 目录创建失败，检查父目录是否存在：" + dir.getParentFile().exists());
        }
        return isCreated;
    }


    /**
     * 删除指定的文件或目录
     * @param path 路径
     * @return 删除是否成功
     */
    public static boolean delete(String path) {
        File target = new File(path);
        if (!target.exists()) return false;

        if (target.isDirectory()) {
            return deleteDirectory(target, 0);
        } else {
            return deleteFile(target);
        }
    }

    /**
     * 删除指定的文件
     *
     * @param file 待删除的文件对象
     * @return 如果文件被成功删除，则返回true；否则返回false
     */
    private static boolean deleteFile(File file) {
        return file.delete();
    }

    /**
     * 递归删除目录下的所有文件及子目录下所有文件
     *
     * @param dir    要删除的目录文件路径
     * @param depth  递归深度
     * @return 删除成功返回true，否则返回false
     */
    private static boolean deleteDirectory(File dir, int depth) {
        if (depth > MAX_RECURSION_DEPTH) {
            Log.w(TAG, "Exceed max recursion depth: " + dir.getPath());
            return false;
        }

        File[] files = dir.listFiles();
        if (files != null) {
            for (File child : files) {
                if (child.isDirectory()) {
                    deleteDirectory(child, depth + 1);
                } else {
                    deleteFile(child);
                }
            }
        }
        return dir.delete();
    }

    /**
     * 写入字符串到文件
     * @param content 字符串
     * @param path 路径
     * @return 写入是否成功
     */
    public static boolean writeStringToFile(String content, String path) {
        return writeStringToFile(content, path, false);
    }

    /**
     * 写入字符串到文件 可以选择追加
     * @param content 字符串
     * @param path 路径
     * @param append 是否追加
     * @return 写入是否成功
     */
    public static boolean writeStringToFile(String content, String path, boolean append) {
        if (path == null) {
            Log.e(TAG, "writeStringToFile: path is null");
            return false;
        }
        if (!exists(path)) {
            createFile(path);
        }
        try (BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(path, append), "UTF-8"))) {
            writer.write(content);
            return true;
        } catch (IOException e) {
            Log.e(TAG, "Write file error: " + path, e);
            return false;
        }
    }

    /**
     * 读取文件内容
     * @param path 路径
     * @return 文件内容
     */
    public static String readFileToString(String path) {
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(path), "UTF-8"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
            if (content.length() > 0) {
                content.setLength(content.length() - 1); // 移除最后的换行符
            }
            return content.toString();
        } catch (IOException e) {
            Log.e(TAG, "Read file error: " + path, e);
            return null;
        }
    }

    /**
     * 文件复制
     * @param sourcePath 源文件路径
     * @param destPath 目标文件路径
     * @return 复制是否成功
     */
    public static boolean copy(String sourcePath, String destPath) {
        File source = new File(sourcePath);
        File dest = new File(destPath);

        if (!source.exists()) return false;

        try (InputStream in = new FileInputStream(source);
             OutputStream out = new FileOutputStream(dest)) {

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
            return true;
        } catch (IOException e) {
            Log.e(TAG, "Copy file error: " + sourcePath + " -> " + destPath, e);
            return false;
        }
    }

    /**
     * 文件移动
     * @param sourcePath 源文件路径
     * @param destPath 目标文件路径
     * @return 移动是否成功
     */
    public static boolean move(String sourcePath, String destPath) {
        if (copy(sourcePath, destPath)) {
            return delete(sourcePath);
        }
        return false;
    }

    /**
     * 重命名文件
     * @param oldPath 原文件路径
     * @param newPath 新文件路径
     * @return 重命名是否成功
     */
    public static boolean rename(String oldPath, String newPath) {
        File file = new File(oldPath);
        File newFile = new File(newPath);
        if (newFile.exists()) return false;
        return file.renameTo(newFile);
    }

    /**
     * 获取文件大小
     * @param path 路径
     * @return 文件大小
     */
    public static long getFileSize(String path) {
        File file = new File(path);
        return file.isFile() ? file.length() : -1;
    }

    /**
     * 获取目录大小
     * @param path 路径
     * @return 目录大小
     */
    public static long getDirectorySize(String path) {
        return calculateDirSize(new File(path), 0);
    }

    /**
     * 递归计算目录大小
     * @param dir
     * @param depth
     * @return
     */
    private static long calculateDirSize(File dir, int depth) {
        if (depth > MAX_RECURSION_DEPTH) {
            Log.w(TAG, "Exceed max recursion depth: " + dir.getPath());
            return 0;
        }

        long size = 0;
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    size += calculateDirSize(file, depth + 1);
                } else {
                    size += file.length();
                }
            }
        }
        return size;
    }

    /**
     * 获取文件扩展名
     * @param path 路径
     * @return 文件扩展名
     */
    public static String getFileExtension(String path) {
        String fileName = new File(path).getName();
        int dotIndex = fileName.lastIndexOf('.');
        return (dotIndex > 0) ? fileName.substring(dotIndex + 1) : "";
    }

    /**
     * 获取文件MIME类型
     * @param path 路径
     * @return mime类型
     */
    public static String getMimeType(String path) {
        String type = URLConnection.guessContentTypeFromName(path);
        if (type == null) {
            if (isDirectory(path)) {
                type = "directory";
            } else {
                String ext = getFileExtension(path).toLowerCase();
                switch (ext) {
                    case "apk":
                        return "application/vnd.android.package-archive";
                    case "dex":
                        return "application/octet-stream";
                    case "mp4":
                        return "video/mp4";
                    case "jpg":
                    case "jpeg":
                        return "image/jpeg";
                    case "png":
                        return "image/png";
                    default:
                        type = "unknown";
                }
            }
        }
        return type;
    }

    /**
     * 文件是否存在
     * @param path 路径
     * @return 是否存在
     */
    public static boolean exists(String path) {
        if (TextUtils.isEmpty(path)) {
            return false;
        }
        return new File(path).exists();
    }

    /**
     * 是否文件
     * @param path 路径
     * @return 是否文件 true 是 false 不是
     */
    public static boolean isFile(String path) {
        return new File(path).isFile();
    }

    /**
     * 是否目录
     * @param path 路径
     * @return 是否目录 true 是 false 不是
     */
    public static boolean isDirectory(String path) {
        return new File(path).isDirectory();
    }

    /**
     * 列出目录文件
     * @param dirPath 目录路径
     * @return 文件列表
     */
    public static List<String> listFiles(String dirPath) {
        File dir = new File(dirPath);
        if (!dir.isDirectory()) return Collections.emptyList();

        List<String> files = new ArrayList<>();
        File[] fileArray = dir.listFiles();
        if (fileArray != null) {
            for (File file : fileArray) {
                files.add(file.getAbsolutePath());
            }
        }
        return files;
    }

    /**
     * 递归列出所有文件
     * @param dirPath 目录路径
     * @return 文件列表
     */
    public static List<String> listAllFiles(String dirPath) {
        List<String> fileList = new ArrayList<>();
        listFilesRecursive(new File(dirPath), fileList, 0);
        return fileList;
    }

    /**
     * 递归列出所有文件
     * @param dir 目录
     * @param result 文件列表
     * @param depth 递归深度
     */
    private static void listFilesRecursive(File dir, List<String> result, int depth) {
        if (depth > MAX_RECURSION_DEPTH) return;

        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                result.add(file.getAbsolutePath());
                if (file.isDirectory()) {
                    listFilesRecursive(file, result, depth + 1);
                }
            }
        }
    }

    /**
     * 获取文件名
     * @param path 路径
     * @return 文件名
     */
    public static String getFileName(String path) {
        return new File(path).getName();
    }

    /**
     * 获取父目录路径
     * @param path 路径
     * @return 父目录路径
     */
    public static String getParentPath(String path) {
        return new File(path).getParent();
    }

    /**
     * 文件MD5校验
     * @param path 路径
     * @return 文件MD5
     */
    public static String getFileMD5(String path) {
        try (InputStream in = new FileInputStream(path)) {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
            }
            return bytesToHex(digest.digest());
        } catch (IOException | NoSuchAlgorithmException e) {
            Log.e(TAG, "MD5 calculation error: " + path, e);
            return null;
        }
    }

    /**
     * 将字节数组转换为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * 创建一个临时文件
     *
     * @param prefix 临时文件的前缀名
     * @param suffix 临时文件的后缀名
     * @return 返回创建的临时文件对象，如果创建失败则返回null
     *
     * 此方法用于生成一个具有指定前缀和后缀的临时文件
     * 如果文件创建过程中发生IO异常，将会记录错误日志并返回null
     */
    public static File createTempFile(String prefix, String suffix) {
        try {
            return File.createTempFile(prefix, suffix);
        } catch (IOException e) {
            Log.e(TAG, "Create temp file error", e);
            return null;
        }
    }

    /**
     * 将文件大小转换为人类可读的格式（例如：1.23 MB）
     * @param sizeBytes 文件大小（字节）
     * @return 格式化后的字符串，如 "1.5 KB" 或 "250 MB"
     */
    public static String getHumanReadableSize(long sizeBytes) {
        if (sizeBytes <= 0) {
            return "0 B";
        }
        String[] units = {"B", "KB", "MB", "GB", "TB", "PB", "EB"};
        int unitIndex = 0;
        double size = sizeBytes;

        // 循环计算直到找到合适的单位
        while (size >= 1024 && unitIndex < units.length - 1) {
            unitIndex++;
            size /= 1024;
        }

        // 格式化数值（自动删除末尾的零）
        DecimalFormat df = new DecimalFormat("#.##");
        df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US));
        df.setRoundingMode(RoundingMode.HALF_UP);
        String formattedSize = df.format(size);

        return formattedSize + " " + units[unitIndex];
    }

    /**
     * 获取应用内部存储的文件目录路径（无需权限）
     * 路径示例：/data/data/<应用包名>/files
     * 特点：
     * - 始终可用，无需任何权限
     * - 应用卸载时自动删除
     * - 适合存储敏感数据或小文件
     *
     * @param context 上下文对象
     * @return 内部存储的持久化文件目录绝对路径
     */
    public static String getInternalFilesPath(Context context) {
        return context.getFilesDir().getAbsolutePath();
    }

    /**
     * 获取应用内部存储的缓存目录路径（无需权限）
     * 路径示例：/data/data/<应用包名>/cache
     * 特点：
     * - 始终可用，无需任何权限
     * - 系统可能自动清理该目录
     * - 应用卸载时自动删除
     *
     * @param context 上下文对象
     * @return 内部存储的临时缓存目录绝对路径
     */
    public static String getInternalCachePath(Context context) {
        return context.getCacheDir().getAbsolutePath();
    }

    /**
     * 获取应用外部存储的私有文件目录路径（无需权限）
     * 路径示例：/storage/emulated/0/Android/data/<应用包名>/files
     * 或 /sdcard/Android/data/<应用包名>/files（根据不同设备）
     * 特点：
     * - 需要外部存储处于可用状态（通过返回值null判断）
     * - 无需声明权限（API 19+）
     * - 应用卸载时自动删除
     * - 适合存储图片、音视频等较大文件
     *
     * @param context 上下文对象
     * @return 外部存储的私有文件目录路径（若不可用返回null）
     */
    public static String getExternalFilesPath(Context context) {
        if (isExternalStorageAvailable()) {
            File dir = context.getExternalFilesDir(null);
            return dir != null ? dir.getAbsolutePath() : null;
        }
        return null;
    }

    /**
     * 获取应用外部存储的私有缓存目录路径（无需权限）
     * 路径示例：/storage/emulated/0/Android/data/<应用包名>/cache
     * 特点：
     * - 需要外部存储处于可用状态（通过返回值null判断）
     * - 无需声明权限（API 19+）
     * - 系统可能自动清理该目录
     * - 应用卸载时自动删除
     *
     * @param context 上下文对象
     * @return 外部存储的私有缓存目录路径（若不可用返回null）
     */
    public static String getExternalCachePath(Context context) {
        if (isExternalStorageAvailable()) {
            File dir = context.getExternalCacheDir();
            return dir != null ? dir.getAbsolutePath() : null;
        }
        return null;
    }

    /**
     * 检查外部存储是否已挂载可写
     * 状态说明：
     * - MEDIA_MOUNTED:     存储可用
     * - MEDIA_MOUNTED_READ_ONLY: 只读模式（不可写）
     * - 其他状态:          存储不可用
     */
    private static boolean isExternalStorageAvailable() {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

}